package fx.utils;

import com.alibaba.fastjson.JSONObject;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;

/**
 * <desc>
 * des加解密帮助类
 * </desc>
 *
 * @createDate 2017/09/19
 */
public class DesEncryptUtil {

    // 向量
    public final static String iv = "0123456789123456";
    // 密钥
    public final static String secretKey = "0123456789123456";

    //QRCODE 初始秘钥
    public static final String QRCODE_INIT_KEY = "AB8E45C5D8A9A898";


    /**
     * <desc>
     * 获取加密key
     * key[0]= INIT_KEY[0]^PROJ_NO[0]
     * key[1]= INIT_KEY[1]^PROJ_NO[1]
     * key[2]= INIT_KEY[2]^PROJ_NO[2]
     * key[3]= INIT_KEY[3]^PROJ_NO[3]
     * key[4]= INIT_KEY[4]^ (~PROJ_NO[0])
     * key[5]= INIT_KEY[5]^ (~PROJ_NO[1])
     * key[6]= INIT_KEY[6]^ (~PROJ_NO[2])
     * key[7]= INIT_KEY[7]^ (~PROJ_NO[3])
     * </desc>
     *
     * @param initKey
     * @param projectNo
     * @return
     * @author Jiaqi.X
     * @createDate 2017/12/20
     */
    public static byte[] getQrcodeKey(String initKey, String projectNo) {
        try {
            byte[] initKeyByte = hexStrToBytes(initKey);
            byte[] projectNoByte = hexStrToBytes(projectNo);
            for (int i = 0; i < projectNoByte.length; i++) {
                String ik = hexString2binaryString(bytesToHexStr(new byte[]{initKeyByte[i]}));
                String pro = hexString2binaryString(bytesToHexStr(new byte[]{projectNoByte[i]}));
                initKeyByte[i] = hexStrToBytes(binaryString2hexString(xor(ik, pro)).toUpperCase())[0];
            }
            for (int i = 0; i < projectNoByte.length; i++) {
                String ik = hexString2binaryString(bytesToHexStr(new byte[]{initKeyByte[i + 4]}));
                String pro = hexString2binaryString(bytesToHexStr(new byte[]{projectNoByte[i]}));
                //二进制取反
                pro = inverseBinary(pro);
                initKeyByte[i + 4] = hexStrToBytes(binaryString2hexString(xor(ik, pro)).toUpperCase())[0];
            }
            return initKeyByte;
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * <desc>
     * 十六进制转二进制
     * </desc>
     *
     * @param hexString
     * @return
     * @author Jiaqi.X
     * @createDate 2017/12/20
     */
    public static String hexString2binaryString(String hexString) {
        try {
            if (hexString == null || hexString.length() % 2 != 0)
                return null;
            String bString = "", tmp;
            for (int i = 0; i < hexString.length(); i++) {
                tmp = "0000"
                        + Integer.toBinaryString(Integer.parseInt(hexString
                        .substring(i, i + 1), 16));
                bString += tmp.substring(tmp.length() - 4);
            }
            return bString;
        } catch (Exception e) {
            throw e;
        }
    }


    /**
     * <desc>
     * DES加密
     * </desc>
     *
     * @param datasource byte[]
     * @param password   String
     * @return byte[]
     * @author Jiaqi.X
     * @createDate 2017/09/15
     */
    public static byte[] Encrypt(byte[] datasource, String password) {
        try {
            SecureRandom random = new SecureRandom();
            DESKeySpec desKey = new DESKeySpec(password.getBytes());
            //创建一个密匙工厂，然后用它把DESKeySpec转换成
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey securekey = keyFactory.generateSecret(desKey);
            //Cipher对象实际完成加密操作
            Cipher cipher = Cipher.getInstance("DES");
            //用密匙初始化Cipher对象
            cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
            //现在，获取数据并加密
            //正式执行加密操作
            return cipher.doFinal(datasource);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * DES 解密
     *
     * @param src      byte[]
     * @param password String
     * @return byte[]
     * @throws Exception
     * @author Jiaqi.X
     * @createDate 2017/09/15
     */
    public static byte[] Decrypt(byte[] src, String password) throws Exception {
        // DES算法要求有一个可信任的随机数源
        SecureRandom random = new SecureRandom();
        // 创建一个DESKeySpec对象
        DESKeySpec desKey = new DESKeySpec(password.getBytes());
        // 创建一个密匙工厂
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        // 将DESKeySpec对象转换成SecretKey对象
        SecretKey securekey = keyFactory.generateSecret(desKey);
        // Cipher对象实际完成解密操作
        Cipher cipher = Cipher.getInstance("DES");
        // 用密匙初始化Cipher对象
        cipher.init(Cipher.DECRYPT_MODE, securekey, random);
        // 真正开始解密操作
        return cipher.doFinal(src);
    }


    /**
     * <desc>
     * SDK项目ID解密
     * <desc/>
     *
     * @param data 待解密数据
     * @return
     * @author Biao.R
     * @createDate 2017/11/3
     */
    public static String projectIdDesEncrypt(String data) throws Exception {
        try {
            if (StringUtils.isEmpty(data)) {
                return null;
            }
            byte[] encrypted1 = Base64.decode(data);
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keyspec = new SecretKeySpec(secretKey.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original);
            return originalString.trim().replaceAll(" +", "");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * <desc>
     * 根据待加密的键值对进行云对讲的设备数据通信加密
     * </desc>
     *
     * @param secretContent 密文串 BASE64(AES(content))+MD5
     * @param sKey
     * @param md5Salt
     * @return
     * @throws Exception
     */
    public static String deviceParamAes(String secretContent, String sKey, String md5Salt) throws Exception {
        try {
            return aesEncrypt(secretContent.toString(), sKey, md5Salt);
        } catch (Exception e) {
            return "";
        }
    }


    /**
     * <desc>
     * 将两个二进制数做异或运算
     * </desc>
     *
     * @param anotherBinary
     * @param thisBinary
     * @return
     * @author Jiaqi.X
     * @createDate 2017/12/20
     */
    private static String xor(String anotherBinary, String thisBinary) {
        try {
            String result = "";
            //判断是否为8位二进制，否则左补零
            if (anotherBinary.length() != 8) {
                for (int i = anotherBinary.length(); i < 8; i++) {
                    anotherBinary = "0" + anotherBinary;
                }
            }
            if (thisBinary.length() != 8) {
                for (int i = thisBinary.length(); i < 8; i++) {
                    thisBinary = "0" + thisBinary;
                }
            }
            //异或运算
            for (int i = 0; i < anotherBinary.length(); i++) {
                //如果相同位置数相同，则补0，否则补1
                if (thisBinary.charAt(i) == anotherBinary.charAt(i))
                    result += "0";
                else {
                    result += "1";
                }
            }
            return result;
        } catch (Exception e) {
            throw e;
        }
    }


    /**
     * <desc>
     * 二进制取反
     * </desc>
     *
     * @param binary
     * @return
     * @author Jiaqi.X
     * @createDate 2017/12/20
     */
    public static String inverseBinary(String binary) {
        try {
            StringBuilder bi = new StringBuilder();
            for (int i = 0; i < binary.length(); i++) {
                String b = binary.substring(i, i + 1);
                if (b.equals("0")) {
                    bi.append("1");
                } else {
                    bi.append("0");
                }
            }
            return bi.toString();
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * <desc>
     * 二进制转十六进制
     * </desc>
     *
     * @param bString
     * @return
     * @author Jiaqi.X
     * @createDate 2017/12/20
     */
    public static String binaryString2hexString(String bString) {
        try {
            if (bString == null || bString.equals("") || bString.length() % 8 != 0)
                return null;
            StringBuffer tmp = new StringBuffer();
            int iTmp = 0;
            for (int i = 0; i < bString.length(); i += 4) {
                iTmp = 0;
                for (int j = 0; j < 4; j++) {
                    iTmp += Integer.parseInt(bString.substring(i + j, i + j + 1)) << (4 - j - 1);
                }
                tmp.append(Integer.toHexString(iTmp));
            }
            return tmp.toString();
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * <desc>
     * 把16进制字符串转换成字节数组.
     * </desc>
     *
     * @param hex 待转换的Hex字符串
     * @return 转换得到的byte数组
     * @author LewisLouis
     * @createDate 2017-11-10
     */
    public static byte[] hexStrToBytes(String hex) {
        int len = (hex.length() / 2);
        byte[] result = new byte[len];
        char[] achar = hex.toCharArray();
        for (int i = 0; i < len; i++) {
            int pos = i * 2;
            result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
        }
        return result;
    }

    /**
     * <desc>
     * 将单个十六进制字符转换为字节
     * </desc>
     *
     * @param c 单个字符
     * @return 转换得到的byte
     * @author LewisLouis
     * @createDate 2017-11-10
     */
    private static byte toByte(char c) {
        byte b = (byte) "0123456789ABCDEF".indexOf(c);
        return b;
    }

    /**
     * <desc>
     * 把字节数组转换成16进制字符串.
     * </desc>
     *
     * @param bArray 待转换的byte数组
     * @return 转换得到的Hex字符串
     * @author LewisLouis
     * @createDate 2017-11-10
     */
    public static String bytesToHexStr(byte[] bArray) {
        if (bArray == null) {
            return "";
        }
        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            if (sTemp.length() < 2) {
                sb.append(0);
            }
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }

    /**
     * <desc>
     * 自动后补齐字符串
     * </desc>
     *
     * @param str    字符串
     * @param supply 待填充的字符
     * @param length 要求填充后达到的字符串总长度
     * @return 填充后的字符串
     * @author LewisLouis
     * @createDate 2017-11-10
     */
    public static String fillString(String str, String supply, int length) {
        StringBuffer sb = new StringBuffer(str);
        while (sb.length() < length) {
            sb.append(supply);
        }
        return sb.toString();
    }

    /**
     * <desc>
     * 云端设备AES加密方案
     * </desc>
     *
     * @param sSrc
     * @param sKey
     * @param md5Salt
     * @return
     * @throws Exception
     */
    public static String aesEncrypt(String sSrc, String sKey, String md5Salt) throws Exception {
        if (sKey == null) {
            System.out.print("Key为空null");
            return null;
        }
        // 判断Key是否为16位
        if (sKey.length() != 16) {
            System.out.print("Key长度不是16位");
            return null;
        }
        byte[] raw = sKey.getBytes("utf-8");
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"算法/模式/补码方式"
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));

        StringBuilder stringBuilder = new StringBuilder();
        if (encrypted == null || encrypted.length <= 0) {
            return null;
        }
        for (int i = 0; i < encrypted.length; i++) {
            int v = encrypted[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        // System.out.println("hex码:"+stringBuilder.toString());

        String base64Str = new Base64().encode(encrypted);//此处使用BASE64做转码功能，同时能起到2次加密的作用。
        //  System.out.println("base64结果:"+base64Str);

        String md5Str = Md5EncryptUtil.md5(base64Str + md5Salt).toUpperCase();
        //    System.out.println("md5Str结果:"+md5Str);
        return base64Str + md5Str;
    }

    /**
     * <desc>
     * 云端设备AES加密方案
     * </desc>
     *
     * @param sSrc
     * @param sKey
     * @return
     * @throws Exception
     */
    public static String aesEncryptSDK(String sSrc, String sKey) throws Exception {
        if (sKey == null) {
            System.out.print("Key为空null");
            return null;
        }
        // 判断Key是否为16位
//        if (sKey.length() != 16) {
//            System.out.print("Key长度不是16位");
//            return null;
//        }
        byte[] raw = sKey.getBytes("utf-8");
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"算法/模式/补码方式"
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));

        StringBuilder stringBuilder = new StringBuilder();
        if (encrypted == null || encrypted.length <= 0) {
            return null;
        }
        for (int i = 0; i < encrypted.length; i++) {
            int v = encrypted[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        String base64Str = new Base64().encode(encrypted);//此处使用BASE64做转码功能，同时能起到2次加密的作用。
        return base64Str;
    }

    /**
     * <desc>
     * 云端设备AES解密
     * </desc>
     *
     * @param sSrc
     * @param sKey
     * @return
     * @throws Exception
     */
    public static String aesDecrypt(String sSrc, String sKey) throws Exception {
        try {
            // 判断Key是否正确
            if (sKey == null) {
                System.out.print("Key为空null");
                return null;
            }
//            // 判断Key是否为16位
//            if (sKey.length() != 16) {
//                System.out.print("Key长度不是16位");
//                return null;
//            }
            if (sSrc.length() < 32) {
                System.out.print("密文串错误");
                return null;
            }
            byte[] raw = sKey.getBytes("utf-8");
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
            byte[] encrypted1 = new Base64().decode(sSrc);//先用base64解密
            try {
                byte[] original = cipher.doFinal(encrypted1);
                String originalString = new String(original, "utf-8");
                return originalString;
            } catch (Exception e) {
                System.out.println(e.toString());
                return null;
            }
        } catch (Exception ex) {
            System.out.println(ex.toString());
            return null;
        }
    }

    /**
     * <desc>
     * 返回加密内容sdkV2
     * </desc>
     *
     * @param appsecret 开发者用户密码
     * @return
     * @author Qiang.S
     * @createDate 2018/12/27
     */
    public static Map<String, Object> getEncryptResultSDKV2(Object result, String appsecret) {
        Map<String, Object> resultMap = new HashMap<>();
        try {
            String encryptResult = DesEncryptUtil.aesEncryptSDK(JSONObject.toJSONString(result), appsecret);
            resultMap.put("encryptScript", encryptResult);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultMap;
    }

    /**
     * <desc>
     * APP传参AES解密
     * </desc>
     *
     * @param sSrc
     * @param sKey
     * @return
     * @throws Exception
     */
    public static String aesDecryptApp(String sSrc, String sKey) throws Exception {
        try {
            // 判断Key是否正确
            if (sKey == null) {
                System.out.print("Key为空null");
                return null;
            }
            // 判断Key是否为16位
            if (sKey.length() != 16) {
                System.out.print("Key长度不是16位");
                return null;
            }
            if (sSrc.length() < 32) {
                System.out.print("密文串错误");
                return null;
            }
            byte[] raw = sKey.getBytes("utf-8");
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/NOPadding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
            byte[] encrypted1 = new Base64().decode(sSrc);//先用base64解密
            try {
                byte[] original = cipher.doFinal(encrypted1);
                String originalString = new String(original, "utf-8");
                return originalString;
            } catch (Exception e) {
                System.out.println(e.toString());
                return null;
            }
        } catch (Exception ex) {
            System.out.println(ex.toString());
            return null;
        }
    }

    /**
     * <desc>
     * APP传参AES加密方案
     * </desc>
     *
     * @param sSrc
     * @param sKey
     * @return
     * @throws Exception
     */
    public static String aesEncryptSDKApp(String sSrc, String sKey) throws Exception {
        if (sKey == null) {
            System.out.print("Key为空null");
            return null;
        }
        // 判断Key是否为16位
        if (sKey.length() != 16) {
            System.out.print("Key长度不是16位");
            return null;
        }
        byte[] raw = sKey.getBytes("utf-8");
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/NOPadding");//"算法/模式/补码方式"
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));

        StringBuilder stringBuilder = new StringBuilder();
        if (encrypted == null || encrypted.length <= 0) {
            return null;
        }
        for (int i = 0; i < encrypted.length; i++) {
            int v = encrypted[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        String base64Str = new Base64().encode(encrypted);//此处使用BASE64做转码功能，同时能起到2次加密的作用。
        return base64Str;
    }

    /**
     * <desc>
     * userKey
     * </desc>
     *
     * @param datasource String userId
     * @param password   String projectKey
     * @return
     * @author Jiaqi.X
     * @createDate 2018/05/25
     */
    public static String getUserKey(String datasource, String password) {
        try {
            SecureRandom random = new SecureRandom();
            DESKeySpec desKey = new DESKeySpec(hexStrToBytes(password));
            //创建一个密匙工厂，然后用它把DESKeySpec转换成
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey securekey = keyFactory.generateSecret(desKey);
            //Cipher对象实际完成加密操作
            Cipher cipher = Cipher.getInstance("DES");
            //用密匙初始化Cipher对象
            cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
            //现在，获取数据并加密
            //正式执行加密操作
            return bytesToHexStr(cipher.doFinal(hexStrToBytes(datasource))).substring(0, 16);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }
}
